1 /* This is free and unencumbered software released into the public domain. */
2 module lmdb_oo;
3 
4 /**
5  * <lmdb_oo.d> - D wrapper for LMDB.
6  *
7  * @author Carsten Schlote <schlote@vahanus.net>
8  * @author Arto Bendiken <arto@bendiken.net>
9  * @see https://sourceforge.net/projects/lmdbxx/
10  */
11 
12 import core.stdc.string;
13 import std.algorithm.mutation;
14 import std.conv;
15 import std.exception;
16 import std.stdio;
17 import std.string;
18 
19 import lmdb;
20 
21 alias mode = mdb_mode_t;
22 
23 /* Exceptions for classes */
24 
25 /** Base class for LMDB exceptions */
26 class MbdError : Exception {
27 	///
28     mixin basicExceptionCtors;
29     /**
30    * Throws an MbdError based on the given LMDB return code.
31    */
32     static void raise(string origin, int rc) {
33         string msg = origin; msg ~= "(" ~ fromStringz(mdb_strerror(rc)) ~ ")";
34         switch (rc) {
35         case MDB_KEYEXIST:
36             throw new KeyExistError(origin);
37         case MDB_NOTFOUND:
38             throw new NotFoundError(origin);
39         case MDB_CORRUPTED:
40             throw new CorruptedError(origin);
41         case MDB_PANIC:
42             throw new PanicError(origin);
43         case MDB_VERSION_MISMATCH:
44             throw new VersionMismatchError(origin);
45         case MDB_MAP_FULL:
46             throw new MapFullError(origin);
47         case MDB_BAD_DBI:
48             throw new BadDbiError(origin);
49         default:
50             throw new RuntimeError(origin);
51         }
52     }
53 }
54 
55 /** Base class for logic error conditions. */
56 class LogicError : MbdError {
57 public:
58     /** Constructor */
59     pure nothrow @nogc @safe this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
60         super(msg, file, line, next);
61     }
62 }
63 
64 /** Base class for fatal error conditions. */
65 class FatalError : MbdError {
66 public:
67     /** Constructor */
68     pure nothrow @nogc @safe this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
69         super(msg, file, line, next);
70     }
71 }
72 
73 /** Base class for runtime error conditions. */
74 class RuntimeError : MbdError {
75 public:
76     /** Constructor */
77     pure nothrow @nogc @safe this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
78         super(msg, file, line, next);
79     }
80 }
81 
82 /** Exception class for `MDB_KEYEXIST` errors.
83  * @see http://symas.com/mdb/doc/group__errors.html#ga05dc5bbcc7da81a7345bd8676e8e0e3b
84  */
85 final class KeyExistError : RuntimeError {
86 public:
87     /** Constructor */
88     pure nothrow @nogc @safe this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
89         super(msg, file, line, next);
90     }
91 }
92 
93 /** Exception class for `MDB_NOTFOUND` errors.
94  * @see http://symas.com/mdb/doc/group__errors.html#gabeb52e4c4be21b329e31c4add1b71926
95  */
96 final class NotFoundError : RuntimeError {
97 public:
98     /** Constructor */
99     pure nothrow @nogc @safe this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
100         super(msg, file, line, next);
101     }
102 }
103 
104 /** Exception class for `MDB_CORRUPTED` errors.
105  * @see http://symas.com/mdb/doc/group__errors.html#gaf8148bf1b85f58e264e57194bafb03ef
106  */
107 final class CorruptedError : FatalError {
108 public:
109     /** Constructor */
110     pure nothrow @nogc @safe this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
111         super(msg, file, line, next);
112     }
113 }
114 
115 /** Exception class for `MDB_PANIC` errors.
116  * @see http://symas.com/mdb/doc/group__errors.html#gae37b9aedcb3767faba3de8c1cf6d3473
117  */
118 final class PanicError : FatalError {
119 public:
120     /** Constructor */
121     pure nothrow @nogc @safe this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
122         super(msg, file, line, next);
123     }
124 }
125 
126 /** Exception class for `MDB_VERSION_MISMATCH` errors.
127  * @see http://symas.com/mdb/doc/group__errors.html#ga909b2db047fa90fb0d37a78f86a6f99b
128  */
129 final class VersionMismatchError : FatalError {
130 public:
131     /** Constructor */
132     pure nothrow @nogc @safe this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
133         super(msg, file, line, next);
134     }
135 }
136 
137 /** Exception class for `MDB_MAP_FULL` errors.
138  * @see http://symas.com/mdb/doc/group__errors.html#ga0a83370402a060c9175100d4bbfb9f25
139  */
140 final class MapFullError : RuntimeError {
141 public:
142     /** Constructor */
143     pure nothrow @nogc @safe this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
144         super(msg, file, line, next);
145     }
146 }
147 
148 /** Exception class for `MDB_BAD_DBI` errors.
149  * @since 0.9.14 (2014/09/20)
150  * @see http://symas.com/mdb/doc/group__errors.html#gab4c82e050391b60a18a5df08d22a7083
151  */
152 final class BadDbiError : RuntimeError {
153 public:
154     /** Constructor */
155     pure nothrow @nogc @safe this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
156         super(msg, file, line, next);
157     }
158 }
159 
160 
161 /* ------------------------------------------------------------------ */
162 
163 /* Procedural Interface: Metadata */
164 
165 // TODO: mdb_version()
166 // TODO: mdb_strerror()
167 
168 
169 /* Procedural Interface: Environment */
170 
171 // TODO: mdb_env_set_assert()
172 // TODO: mdb_reader_list()
173 // TODO: mdb_reader_check()
174 
175 /** Wrapper for mdb_env_create
176  * @throws lmdb_oo.error on failure
177  * @see http://symas.com/mdb/doc/group__mdb.html#gaad6be3d8dcd4ea01f8df436f41d158d4
178  */
179 static void env_create(MDB_env** env) {
180     const int rc = mdb_env_create(env);
181     if (rc != MDB_SUCCESS) {
182         MbdError.raise("mdb_env_create", rc);
183     }
184 }
185 
186 /** Wrapper for mdb_env_open
187  * @throws lmdb_oo.MbdError on failure
188  * @see http://symas.com/mdb/doc/group__mdb.html#ga32a193c6bf4d7d5c5d579e71f22e9340
189  */
190 static void env_open(MDB_env* env, const char* path, const uint flags, const mode mode) {
191     const int rc = mdb_env_open(env, path, flags, mode);
192     if (rc != MDB_SUCCESS) {
193         MbdError.raise("mdb_env_open", rc);
194     }
195 }
196 
197 /** Wrapper for mdb_env_copy
198  * @throws lmdb_oo.MbdError on failure
199  * @see http://symas.com/mdb/doc/group__mdb.html#ga3bf50d7793b36aaddf6b481a44e24244
200  * @see http://symas.com/mdb/doc/group__mdb.html#ga5d51d6130325f7353db0955dbedbc378
201  */
202 static void env_copy(MDB_env* env, const char* path, const uint flags = 0) {
203     const int rc = mdb_env_copy2(env, path, flags);
204     if (rc != MDB_SUCCESS) {
205         MbdError.raise("mdb_env_copy2", rc);
206     }
207 }
208 
209 /** Wrapper for mdb_env_copy_fd
210  * @throws lmdb_oo.MbdError on failure
211  * @see http://symas.com/mdb/doc/group__mdb.html#ga5040d0de1f14000fa01fc0b522ff1f86
212  * @see http://symas.com/mdb/doc/group__mdb.html#ga470b0bcc64ac417de5de5930f20b1a28
213  */
214 static void env_copy_fd(MDB_env* env, const mdb_filehandle_t fd, const uint flags = 0) {
215     const int rc = mdb_env_copyfd2(env, fd, flags);
216     if (rc != MDB_SUCCESS) {
217         MbdError.raise("mdb_env_copyfd2", rc);
218     }
219 }
220 
221 /** Wrapper for mdb_env_stat
222  * @throws lmdb_oo.MbdError on failure
223  * @see http://symas.com/mdb/doc/group__mdb.html#gaf881dca452050efbd434cd16e4bae255
224  */
225 static void env_stat(MDB_env* env, MDB_stat* stat) {
226     const int rc = mdb_env_stat(env, stat);
227     if (rc != MDB_SUCCESS) {
228         MbdError.raise("mdb_env_stat", rc);
229     }
230 }
231 
232 /** Wrapper for env_info
233  * @throws lmdb_oo.MbdError on failure
234  * @see http://symas.com/mdb/doc/group__mdb.html#ga18769362c7e7d6cf91889a028a5c5947
235  */
236 static void env_info(MDB_env* env, MDB_envinfo* stat) {
237     const int rc = mdb_env_info(env, stat);
238     if (rc != MDB_SUCCESS) {
239         MbdError.raise("mdb_env_info", rc);
240     }
241 }
242 
243 /** Wrapper for mdb_env_sync
244  * @throws lmdb_oo.MbdError on failure
245  * @see http://symas.com/mdb/doc/group__mdb.html#ga85e61f05aa68b520cc6c3b981dba5037
246  */
247 static void env_sync(MDB_env* env, const bool force = true) {
248     const int rc = mdb_env_sync(env, force);
249     if (rc != MDB_SUCCESS) {
250         MbdError.raise("mdb_env_sync", rc);
251     }
252 }
253 
254 /** Wrapper for mdb_env_close
255  * @see http://symas.com/mdb/doc/group__mdb.html#ga4366c43ada8874588b6a62fbda2d1e95
256  */
257 static void env_close(MDB_env* env) nothrow {
258     mdb_env_close(env);
259 }
260 
261 /** Wrapper for mdb_env_set_flags
262  * @throws lmdb_oo.MbdError on failure
263  * @see http://symas.com/mdb/doc/group__mdb.html#ga83f66cf02bfd42119451e9468dc58445
264  */
265 static void env_set_flags(MDB_env* env, const uint flags, const bool onoff = true) {
266     const int rc = mdb_env_set_flags(env, flags, onoff ? 1 : 0);
267     if (rc != MDB_SUCCESS) {
268         MbdError.raise("mdb_env_set_flags", rc);
269     }
270 }
271 
272 /** Wrapper for mdb_env_get_flags
273  * @throws lmdb_oo.MbdError on failure
274  * @see http://symas.com/mdb/doc/group__mdb.html#ga2733aefc6f50beb49dd0c6eb19b067d9
275  */
276 static void env_get_flags(MDB_env* env, uint* flags) {
277     const int rc = mdb_env_get_flags(env, flags);
278     if (rc != MDB_SUCCESS) {
279         MbdError.raise("mdb_env_get_flags", rc);
280     }
281 }
282 
283 /** Wrapper for mdb_env_get_path
284  * @throws lmdb_oo.MbdError on failure
285  * @see http://symas.com/mdb/doc/group__mdb.html#gac699fdd8c4f8013577cb933fb6a757fe
286  */
287 static void env_get_path(MDB_env* env, const char** path) {
288     const int rc = mdb_env_get_path(env, path);
289     if (rc != MDB_SUCCESS) {
290         MbdError.raise("mdb_env_get_path", rc);
291     }
292 }
293 
294 /** Wrapper for mdb_env_get_fd
295  * @throws lmdb_oo.MbdError on failure
296  * @see http://symas.com/mdb/doc/group__mdb.html#gaf1570e7c0e5a5d860fef1032cec7d5f2
297  */
298 static void env_get_fd(MDB_env* env, mdb_filehandle_t* fd) {
299     const int rc = mdb_env_get_fd(env, fd);
300     if (rc != MDB_SUCCESS) {
301         MbdError.raise("mdb_env_get_fd", rc);
302     }
303 }
304 
305 /** Wrapper for mdb_env_set_mapsize
306  * @throws lmdb_oo.MbdError on failure
307  * @see http://symas.com/mdb/doc/group__mdb.html#gaa2506ec8dab3d969b0e609cd82e619e5
308  */
309 static void env_set_mapsize(MDB_env* env, const size_t size) {
310     const int rc = mdb_env_set_mapsize(env, size);
311     if (rc != MDB_SUCCESS) {
312         MbdError.raise("mdb_env_set_mapsize", rc);
313     }
314 }
315 
316 /** Wrapper for mdb_env_set_max_readers
317  * @throws lmdb_oo.MbdError on failure
318  * @see http://symas.com/mdb/doc/group__mdb.html#gae687966c24b790630be2a41573fe40e2
319  */
320 static void env_set_max_readers(MDB_env* env, const uint count) {
321     const int rc = mdb_env_set_maxreaders(env, count);
322     if (rc != MDB_SUCCESS) {
323         MbdError.raise("mdb_env_set_maxreaders", rc);
324     }
325 }
326 
327 /** Wrapper for mdb_env_get_max_readers
328  * @throws lmdb_oo.MbdError on failure
329  * @see http://symas.com/mdb/doc/group__mdb.html#ga70e143cf11760d869f754c9c9956e6cc
330  */
331 static void env_get_max_readers(MDB_env* env, uint* count) {
332     const int rc = mdb_env_get_maxreaders(env, count);
333     if (rc != MDB_SUCCESS) {
334         MbdError.raise("mdb_env_get_maxreaders", rc);
335     }
336 }
337 
338 /** Wrapper for mdb_env_set_max_dbs
339  * @throws lmdb_oo.MbdError on failure
340  * @see http://symas.com/mdb/doc/group__mdb.html#gaa2fc2f1f37cb1115e733b62cab2fcdbc
341  */
342 static void env_set_max_dbs(MDB_env* env, const MDB_dbi count) {
343     const int rc = mdb_env_set_maxdbs(env, count);
344     if (rc != MDB_SUCCESS) {
345         MbdError.raise("mdb_env_set_maxdbs", rc);
346     }
347 }
348 
349 /** Wrapper for mdb_env_get_max_keysize
350  * @see http://symas.com/mdb/doc/group__mdb.html#gaaf0be004f33828bf2fb09d77eb3cef94
351  */
352 static uint env_get_max_keysize(MDB_env* env) {
353     const int rc = mdb_env_get_maxkeysize(env);
354     assert(rc >= 0);
355     return cast(uint) rc;
356 }
357 
358 /** Wrapper for mdb_env_set_userctx
359  * @throws lmdb_oo.MbdError on failure
360  * @since 0.9.11 (2014/01/15)
361  * @see http://symas.com/mdb/doc/group__mdb.html#gaf2fe09eb9c96eeb915a76bf713eecc46
362  */
363 static void env_set_userctx(MDB_env* env, void* ctx) {
364     const int rc = mdb_env_set_userctx(env, ctx);
365     if (rc != MDB_SUCCESS) {
366         MbdError.raise("mdb_env_set_userctx", rc);
367     }
368 }
369 
370 /** Wrapper for mdb_env_get_userctx
371  * @since 0.9.11 (2014/01/15)
372  * @see http://symas.com/mdb/doc/group__mdb.html#ga45df6a4fb150cda2316b5ae224ba52f1
373  */
374 static void* env_get_userctx(MDB_env* env) {
375     return mdb_env_get_userctx(env);
376 }
377 
378 
379 /* Procedural Interface: Transactions */
380 
381 /** Wrapper for mdb_txn_begin
382  * @throws lmdb_oo.MbdError on failure
383  * @see http://symas.com/mdb/doc/group__mdb.html#gad7ea55da06b77513609efebd44b26920
384  */
385 static void txn_begin(MDB_env* env, MDB_txn* parent, const uint flags, MDB_txn** txn) {
386     const int rc = mdb_txn_begin(env, parent, flags, txn);
387     if (rc != MDB_SUCCESS) {
388         MbdError.raise("mdb_txn_begin", rc);
389     }
390 }
391 
392 /** Wrapper for mdb_txn_env
393  * @see http://symas.com/mdb/doc/group__mdb.html#gaeb17735b8aaa2938a78a45cab85c06a0
394  */
395 static MDB_env* txn_env(MDB_txn* txn) nothrow {
396     return mdb_txn_env(txn);
397 }
398 
399 /** Wrapper for mdb_txn_id
400  * @note Only available in HEAD, not yet in any 0.9.x release (as of 0.9.16).
401  */
402 static size_t txn_id(MDB_txn* txn) nothrow {
403     return mdb_txn_id(txn);
404 }
405 
406 /** Wrapper for mdb_txn_commit
407  * @throws lmdb_oo.MbdError on failure
408  * @see http://symas.com/mdb/doc/group__mdb.html#ga846fbd6f46105617ac9f4d76476f6597
409  */
410 static void txn_commit(MDB_txn* txn) {
411     const int rc = mdb_txn_commit(txn);
412     if (rc != MDB_SUCCESS) {
413         MbdError.raise("mdb_txn_commit", rc);
414     }
415 }
416 
417 /** Wrapper for mdb_txn_abort
418  * @see http://symas.com/mdb/doc/group__mdb.html#ga73a5938ae4c3239ee11efa07eb22b882
419  */
420 static void txn_abort(MDB_txn* txn) nothrow {
421     mdb_txn_abort(txn);
422 }
423 
424 /** Wrapper for mdb_txn_reset
425  * @see http://symas.com/mdb/doc/group__mdb.html#ga02b06706f8a66249769503c4e88c56cd
426  */
427 static void txn_reset(MDB_txn* txn) nothrow {
428     mdb_txn_reset(txn);
429 }
430 
431 /** Wrapper for mdb_txn_renew
432  * @throws lmdb_oo.MbdError on failure
433  * @see http://symas.com/mdb/doc/group__mdb.html#ga6c6f917959517ede1c504cf7c720ce6d
434  */
435 static void txn_renew(MDB_txn* txn) {
436     const int rc = mdb_txn_renew(txn);
437     if (rc != MDB_SUCCESS) {
438         MbdError.raise("mdb_txn_renew", rc);
439     }
440 }
441 
442 
443 /* Procedural Interface: Databases */
444 
445 /** Wrapper for mdb_dbi_open
446  * @throws lmdb_oo.MbdError on failure
447  * @see http://symas.com/mdb/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a
448  */
449 static void dbi_open(MDB_txn* txn, const char* name, const uint flags, MDB_dbi* dbi) {
450     const int rc = mdb_dbi_open(txn, name, flags, dbi);
451     if (rc != MDB_SUCCESS) {
452         MbdError.raise("mdb_dbi_open", rc);
453     }
454 }
455 
456 /** Wrapper for mdb_stat
457  * @throws lmdb_oo.MbdError on failure
458  * @see http://symas.com/mdb/doc/group__mdb.html#gae6c1069febe94299769dbdd032fadef6
459  */
460 static void dbi_stat(MDB_txn* txn, const MDB_dbi dbi, MDB_stat* result) {
461     const int rc = mdb_stat(txn, dbi, result);
462     if (rc != MDB_SUCCESS) {
463         MbdError.raise("mdb_stat", rc);
464     }
465 }
466 
467 /** Wrapper for mdb_flags
468  * @throws lmdb_oo.MbdError on failure
469  * @see http://symas.com/mdb/doc/group__mdb.html#ga95ba4cb721035478a8705e57b91ae4d4
470  */
471 static void dbi_flags(MDB_txn* txn, const MDB_dbi dbi, uint* flags) {
472     const int rc = mdb_dbi_flags(txn, dbi, flags);
473     if (rc != MDB_SUCCESS) {
474         MbdError.raise("mdb_dbi_flags", rc);
475     }
476 }
477 
478 /** Wrapper for mdb_close
479  * @see http://symas.com/mdb/doc/group__mdb.html#ga52dd98d0c542378370cd6b712ff961b5
480  */
481 static void dbi_close(MDB_env* env, const MDB_dbi dbi) nothrow {
482     mdb_dbi_close(env, dbi);
483 }
484 
485 /** Wrapper for mdb_drop
486  * @see http://symas.com/mdb/doc/group__mdb.html#gab966fab3840fc54a6571dfb32b00f2db
487  */
488 static void dbi_drop(MDB_txn* txn, const MDB_dbi dbi, const bool del = false) {
489     const int rc = mdb_drop(txn, dbi, del ? 1 : 0);
490     if (rc != MDB_SUCCESS) {
491         MbdError.raise("mdb_drop", rc);
492     }
493 }
494 
495 /** Wrapper for mdb_set_compare
496  * @throws lmdb_oo.MbdError on failure
497  * @see http://symas.com/mdb/doc/group__mdb.html#ga68e47ffcf72eceec553c72b1784ee0fe
498  */
499 static void dbi_set_compare(MDB_txn* txn, const MDB_dbi dbi, MDB_cmp_func* cmp = null) {
500     const int rc = mdb_set_compare(txn, dbi, cmp);
501     if (rc != MDB_SUCCESS) {
502         MbdError.raise("mdb_set_compare", rc);
503     }
504 }
505 
506 /** Wrapper for mdb_set_dupsort
507  * @throws lmdb_oo.MbdError on failure
508  * @see http://symas.com/mdb/doc/group__mdb.html#gacef4ec3dab0bbd9bc978b73c19c879ae
509  */
510 static void dbi_set_dupsort(MDB_txn* txn, const MDB_dbi dbi, MDB_cmp_func* cmp = null) {
511     const int rc = mdb_set_dupsort(txn, dbi, cmp);
512     if (rc != MDB_SUCCESS) {
513         MbdError.raise("mdb_set_dupsort", rc);
514     }
515 }
516 
517 /** Wrapper for mdb_set_relfunc
518  * @throws lmdb_oo.MbdError on failure
519  * @see http://symas.com/mdb/doc/group__mdb.html#ga697d82c7afe79f142207ad5adcdebfeb
520  */
521 static void dbi_set_relfunc(MDB_txn* txn, const MDB_dbi dbi, MDB_rel_func* rel) {
522     const int rc = mdb_set_relfunc(txn, dbi, rel);
523     if (rc != MDB_SUCCESS) {
524         MbdError.raise("mdb_set_relfunc", rc);
525     }
526 }
527 
528 /** Wrapper for mdb_dbi_set_relctx
529  * @throws lmdb_oo.MbdError on failure
530  * @see http://symas.com/mdb/doc/group__mdb.html#ga7c34246308cee01724a1839a8f5cc594
531  */
532 static void dbi_set_relctx(MDB_txn* txn, const MDB_dbi dbi, void* ctx) {
533     const int rc = mdb_set_relctx(txn, dbi, ctx);
534     if (rc != MDB_SUCCESS) {
535         MbdError.raise("mdb_set_relctx", rc);
536     }
537 }
538 
539 /** Wrapper for mdb_get
540  * @retval true  if the key/value pair was retrieved
541  * @retval false if the key wasn't found
542  * @see http://symas.com/mdb/doc/group__mdb.html#ga8bf10cd91d3f3a83a34d04ce6b07992d
543  */
544 static bool dbi_get(MDB_txn* txn, const MDB_dbi dbi, const MDB_val* key, MDB_val* data) {
545     const int rc = mdb_get(txn, cast(uint) dbi, cast(MDB_val*) key, data);
546     if (rc != MDB_SUCCESS && rc != MDB_NOTFOUND) {
547         MbdError.raise("mdb_get", rc);
548     }
549     return (rc == MDB_SUCCESS);
550 }
551 
552 /** Wrapper for mdb_put
553  * @retval true  if the key/value pair was inserted
554  * @retval false if the key already existed
555  * @see http://symas.com/mdb/doc/group__mdb.html#ga4fa8573d9236d54687c61827ebf8cac0
556  */
557 static bool dbi_put(MDB_txn* txn, MDB_dbi dbi, MDB_val* key, MDB_val* data, uint flags = 0) {
558     const int rc = mdb_put(txn, dbi, key, data, flags);
559     if (rc != MDB_SUCCESS && rc != MDB_KEYEXIST) {
560         MbdError.raise("mdb_put", rc);
561     }
562     return (rc == MDB_SUCCESS);
563 }
564 
565 /** Wrapper for mdb_del
566  * @retval true  if the key/value pair was removed
567  * @retval false if the key wasn't found
568  * @see http://symas.com/mdb/doc/group__mdb.html#gab8182f9360ea69ac0afd4a4eaab1ddb0
569  */
570 static bool dbi_del(MDB_txn* txn, MDB_dbi dbi, MDB_val* key, MDB_val* data = null) {
571     const int rc = mdb_del(txn, dbi, key, data);
572     if (rc != MDB_SUCCESS && rc != MDB_NOTFOUND) {
573         MbdError.raise("mdb_del", rc);
574     }
575     return (rc == MDB_SUCCESS);
576 }
577 
578 
579 /* -- Procedural Interface: Cursors --------------------------------- */
580 
581 /** Wrapper for cursor_open
582  * @throws lmdb_oo.MbdError on failure
583  */
584 static void cursor_open(MDB_txn* txn, const MDB_dbi dbi, MDB_cursor** cursor) {
585     const int rc = mdb_cursor_open(txn, dbi, cursor);
586     if (rc != MDB_SUCCESS) {
587         MbdError.raise("mdb_cursor_open", rc);
588     }
589 }
590 
591 /** Wrapper for cursor_close
592  * @see http://symas.com/mdb/doc/group__mdb.html#gad685f5d73c052715c7bd859cc4c05188
593  */
594 static void cursor_close(MDB_cursor* cursor) nothrow {
595     mdb_cursor_close(cursor);
596 }
597 
598 /** Wrapper for cursor_renew
599  * @throws lmdb_oo.MbdError on failure
600  * @see http://symas.com/mdb/doc/group__mdb.html#gac8b57befb68793070c85ea813df481af
601  */
602 static void cursor_renew(MDB_txn* txn, MDB_cursor* cursor) {
603     const int rc = mdb_cursor_renew(txn, cursor);
604     if (rc != MDB_SUCCESS) {
605         MbdError.raise("mdb_cursor_renew", rc);
606     }
607 }
608 
609 /** Wrapper for cursor_txn
610  * @see http://symas.com/mdb/doc/group__mdb.html#ga7bf0d458f7f36b5232fcb368ebda79e0
611  */
612 static MDB_txn* cursor_txn(MDB_cursor* cursor) nothrow {
613     return mdb_cursor_txn(cursor);
614 }
615 
616 /** Wrapper for cursor_dbi
617  * @see http://symas.com/mdb/doc/group__mdb.html#ga2f7092cf70ee816fb3d2c3267a732372
618  */
619 static MDB_dbi cursor_dbi(MDB_cursor* cursor) nothrow {
620     return mdb_cursor_dbi(cursor);
621 }
622 
623 /** Wrapper for cursor_get
624  * @throws lmdb_oo.MbdError on failure
625  * @see http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0
626  */
627 static bool cursor_get(MDB_cursor* cursor, MDB_val* key, MDB_val* data, const MDB_cursor_op op) {
628     const int rc = mdb_cursor_get(cursor, key, data, op);
629     if (rc != MDB_SUCCESS && rc != MDB_NOTFOUND) {
630         MbdError.raise("mdb_cursor_get", rc);
631     }
632     return (rc == MDB_SUCCESS);
633 }
634 
635 /** Wrapper for cursor_put
636  * @throws lmdb_oo.MbdError on failure
637  * @see http://symas.com/mdb/doc/group__mdb.html#ga1f83ccb40011837ff37cc32be01ad91e
638  */
639 static void cursor_put(MDB_cursor* cursor, MDB_val* key, MDB_val* data, const uint flags = 0) {
640     const int rc = mdb_cursor_put(cursor, key, data, flags);
641     if (rc != MDB_SUCCESS) {
642         MbdError.raise("mdb_cursor_put", rc);
643     }
644 }
645 
646 /** Wrapper for cursor_del
647  * @throws lmdb_oo.MbdError on failure
648  * @see http://symas.com/mdb/doc/group__mdb.html#ga26a52d3efcfd72e5bf6bd6960bf75f95
649  */
650 static void cursor_del(MDB_cursor* cursor, const uint flags = 0) {
651     const int rc = mdb_cursor_del(cursor, flags);
652     if (rc != MDB_SUCCESS) {
653         MbdError.raise("mdb_cursor_del", rc);
654     }
655 }
656 
657 /** Wrapper for cursor_count
658  * @throws lmdb_oo.MbdError on failure
659  * @see http://symas.com/mdb/doc/group__mdb.html#ga4041fd1e1862c6b7d5f10590b86ffbe2
660  */
661 static void cursor_count(MDB_cursor* cursor, ref size_t count) {
662     const int rc = mdb_cursor_count(cursor, &count);
663     if (rc != MDB_SUCCESS) {
664         MbdError.raise("mdb_cursor_count", rc);
665     }
666 }
667 
668 
669 /* Resource Interface: Values */
670 
671 /** Wrapper class for `MDB_val` structures.
672  *
673  * @note Instances of this class are movable and copyable both.
674  * @see std.algorithm.mutation
675  * @see http://symas.com/mdb/doc/group__mdb.html#structMDB__val
676  */
677 class MdbVal {
678 protected:
679     MDB_val _val;
680 
681 public:
682     /** Default constructor. */
683     this() nothrow {
684     }
685 
686     /** Constructor. Expects a D string */
687     this(const string data) nothrow {
688         this(toStringz(data), data.sizeof);
689     }
690 
691     /** Constructor. Expects a C-string as input. */
692     this(const char* data) nothrow {
693         this(data, strlen(data));
694     }
695 
696     /** Constructor. Expects some data and size */
697     this(const void* data, const size_t size) nothrow {
698 		_val = MDB_val(data, size);
699     }
700 
701     /** Constructor. Use a pointer to other object for a shallow copy */
702     this(MdbVal* data) nothrow {
703 		_val = MDB_val(&data._val);
704     }
705 
706     /** Constructor. Use a reference to a object to do a deep copy */
707     this(ref MdbVal data) nothrow {
708 		_val = data._val; // Triggers copy-constructor of struct dupping the data.
709     }
710 
711     /** Determines whether this value is empty. */
712     bool empty() const nothrow {
713         return _val.size() == 0;
714     }
715 
716     /** Returns the size of the data. */
717     size_t size() const nothrow {
718         return _val.size();
719     }
720 
721     /** Returns a pointer to the embedded struct */
722     T* data(T)() nothrow {
723         return _val.data(T)();
724     }
725 
726     /** Assigns the value. */
727     MdbVal assign(const ref string data) nothrow {
728         return assign(toStringz(data), data.sizeof);
729     }
730     /** Assigns the value. */
731     MdbVal assign(const char* data) nothrow {
732         return assign(data, strlen(data));
733     }
734     /** Assigns the value. */
735     MdbVal assign(T)(const T* data, const size_t size) nothrow {
736         _val.mv_size = size;
737         _val.mv_data = cast(void*) data;
738         return this;
739     }
740 
741 	/** Print it some useful way... */
742 	override string toString() const {
743 		import std.format : format;
744 		return format("%x @ %d %s", _val.data!(ubyte*)(), _val.size(), _val.data!ubyte()[0.._val.size()]);
745 	}
746 }
747 
748 //static assert(std::is_pod<lmdb_oo.MdbVal>::value, "MdbVal must be a POD type");
749 //static assert((lmdb_oo.MdbVal.sizeof) == (lmdb.MDB_val.sizeof), "sizeof(lmdb_oo.MdbVal) != sizeof(MDB_val)");
750 
751 
752 /* Resource Interface: Environment */
753 
754 /**
755  * Resource class for `MDB_env*` handles.
756  *
757  * @note Instances of this class are movable, but not copyable.
758  * @see http://symas.com/mdb/doc/group__internal.html#structMDB__env
759  */
760 class MdbEnv {
761 protected:
762     MDB_env* _handle;
763 
764 public:
765     static uint default_flags;  ///< Default flags
766     static mode default_mode = std.conv.octal!644; /**< -rw-r--r-- */
767 
768     /**
769    * Creates a new LMDB environment.
770    *
771    * @param flags
772    * @throws lmdb_oo.MbdError on failure
773    */
774     static MdbEnv create(const uint flags = default_flags) {
775         MDB_env* handle;
776         env_create(&handle);
777 
778         assert(handle != null);
779 
780         if (flags) {
781             try {
782                 env_set_flags(handle, flags);
783             } catch (const MbdError) {
784                 env_close(handle);
785                 throw new Exception("Problem opeing env");
786             }
787         }
788         return new MdbEnv(handle);
789     }
790 
791     /**
792    * Constructor.
793    *
794    * @param handle a valid `MDB_env*` handle
795    */
796     this(MDB_env* handle) nothrow {
797         _handle = handle;
798     }
799 
800     /**
801    * Move constructor.
802    */
803     //this(MdbEnv&& other) nothrow {
804     //  std::swap(_handle, other._handle);
805     //}
806 
807     /**
808    * Move assignment operator.
809    */
810     //MdbEnv& operator=(MdbEnv&& other) nothrow {
811     //  if (this != &other) {
812     //   std::swap(_handle, other._handle);
813     //  }
814     //  return *this;
815     //}
816 
817     /**
818    * Destructor.
819    */
820     ~this() nothrow {
821         try {
822             close();
823         } catch (Exception ex) {
824         }
825     }
826 
827     /**
828    * Returns the underlying `MDB_env*` handle.
829    */
830     //operator MDB_env*() const nothrow {
831     //  return _handle;
832     //}
833 
834     /**
835    * Returns the underlying `MDB_env*` handle.
836    */
837     MDB_env* handle() const nothrow {
838         return cast(MDB_env*) _handle;
839     }
840 
841     /**
842    * Flushes data buffers to disk.
843    *
844    * @param force
845    * @throws lmdb_oo.MbdError on failure
846    */
847     void sync(const bool force = true) {
848         env_sync(handle(), force);
849     }
850 
851     /**
852    * Closes this environment, releasing the memory map.
853    *
854    * @note this method is idempotent
855    * @post `handle() == null`
856    */
857     void close() nothrow {
858         if (handle()) {
859             env_close(handle());
860             _handle = null;
861         }
862     }
863 
864     /**
865    * Opens this environment.
866    *
867    * @param path
868    * @param flags
869    * @param mode
870    * @throws lmdb_oo.MbdError on failure
871    */
872     MdbEnv open(const char* path, const uint flags = default_flags, const mode mode = default_mode) {
873         env_open(handle(), path, flags, mode);
874         return this;
875     }
876 
877     /**
878    * @param flags
879    * @param onoff
880    * @throws lmdb_oo.MbdError on failure
881    */
882     MdbEnv set_flags(const uint flags, const bool onoff = true) {
883         env_set_flags(handle(), flags, onoff);
884         return this;
885     }
886 
887     /**
888    * @param size
889    * @throws lmdb_oo.MbdError on failure
890    */
891     MdbEnv set_mapsize(const size_t size) {
892         env_set_mapsize(handle(), size);
893         return this;
894     }
895 
896     /**
897    * @param count
898    * @throws lmdb_oo.MbdError on failure
899    */
900     MdbEnv set_max_readers(const uint count) {
901         env_set_max_readers(handle(), count);
902         return this;
903     }
904 
905     /**
906    * @param count
907    * @throws lmdb_oo.MbdError on failure
908    */
909     MdbEnv set_max_dbs(const MDB_dbi count) {
910         env_set_max_dbs(handle(), count);
911         return this;
912     }
913 }
914 
915 
916 /* Resource Interface: Transactions */
917 
918 /**
919  * Resource class for `MDB_txn*` handles.
920  *
921  * @note Instances of this class are movable, but not copyable.
922  * @see http://symas.com/mdb/doc/group__internal.html#structMDB__txn
923  */
924 class MdbTxn {
925 protected:
926     MDB_txn* _handle;
927 
928 public:
929     static uint default_flags;  ///< Default flags
930 
931     /**
932    * Creates a new LMDB transaction.
933    *
934    * @param env the environment handle
935    * @param parent
936    * @param flags
937    * @throws lmdb_oo.MbdError on failure
938    */
939     static MdbTxn begin(MDB_env* env, MDB_txn* parent = null, const uint flags = default_flags) {
940         MDB_txn* handle;
941         txn_begin(env, parent, flags, &handle);
942         assert(handle != null);
943         return new MdbTxn(handle);
944     }
945 
946     /**
947    * Constructor.
948    *
949    * @param handle a valid `MDB_txn*` handle
950    */
951     this(MDB_txn* handle) nothrow {
952         _handle = handle;
953     }
954 
955     /**
956    * Destructor.
957    */
958     ~this() nothrow {
959         if (_handle) {
960             try {
961                 abort();
962             } catch (Exception ex) {
963             }
964             _handle = null;
965         }
966     }
967 
968     /**
969    * Returns the underlying `MDB_txn*` handle.
970    */
971     MDB_txn* opUnary(string s)() if (s == "*") {
972       return _handle;
973     }
974 
975     /**
976    * Returns the underlying `MDB_txn*` handle.
977    */
978     MDB_txn* handle() nothrow {
979       return _handle;
980     }
981 
982     /**
983    * Returns the transaction's `MDB_env*` handle.
984    */
985     MDB_env* env() nothrow {
986         return txn_env(handle());
987     }
988 
989     /**
990    * Commits this transaction.
991    *
992    * @throws lmdb_oo.MbdError on failure
993    * @post `handle() == null`
994    */
995     void commit() {
996         txn_commit(_handle);
997         _handle = null;
998     }
999 
1000     /**
1001    * Aborts this transaction.
1002    *
1003    * @post `handle() == null`
1004    */
1005     void abort() nothrow {
1006         txn_abort(_handle);
1007         _handle = null;
1008     }
1009 
1010     /**
1011    * Resets this read-only transaction.
1012    */
1013     void reset() nothrow {
1014         txn_reset(_handle);
1015     }
1016 
1017     /**
1018    * Renews this read-only transaction.
1019    *
1020    * @throws lmdb_oo.MbdError on failure
1021    */
1022     void renew() {
1023         txn_renew(_handle);
1024     }
1025 }
1026 
1027 
1028 /* Resource Interface: Databases */
1029 
1030 /**
1031  * Resource class for `MDB_dbi` handles.
1032  *
1033  * @note Instances of this class are movable, but not copyable.
1034  * @see http://symas.com/mdb/doc/group__mdb.html#gadbe68a06c448dfb62da16443d251a78b
1035  */
1036 class MdbDbi {
1037 protected:
1038     MDB_dbi _handle;
1039 
1040 public:
1041     static uint default_flags; ///< Default flags
1042     static uint default_put_flags; ///< Default flags
1043 
1044     /** Opens a database handle.
1045    *
1046    * @param txn the transaction handle
1047    * @param name
1048    * @param flags
1049    * @throws lmdb_oo.MbdError on failure
1050    */
1051     static MdbDbi open(MDB_txn* txn, const char* name = null, const uint flags = default_flags) {
1052         MDB_dbi handle;
1053         mdb_dbi_open(txn, name, flags, &handle);
1054         return new MdbDbi(handle);
1055     }
1056     /** Close a database handle
1057     * Param: 
1058     *   env = an MdbEnv
1059     */
1060     void close(MdbEnv env) {
1061         mdb_dbi_close(env.handle(), _handle);
1062     }
1063 
1064     /** Constructor.
1065    * Param:  handle = a valid `MDB_dbi` handle
1066    */
1067     this(const MDB_dbi handle) nothrow {
1068         _handle = handle;
1069     }
1070 
1071     /**
1072    * Destructor.
1073    */
1074     ~this() nothrow {
1075         if (_handle) {
1076             /* No need to call close() here. */
1077         }
1078     }
1079 
1080     /**
1081    * Returns the underlying `MDB_dbi` handle.
1082    */
1083     MDB_dbi handle() nothrow {
1084       return _handle;
1085     }
1086 
1087     /**
1088    * Returns statistics for this database.
1089    *
1090    * @param txn a transaction handle
1091    * @throws lmdb_oo.MbdError on failure
1092    */
1093     MDB_stat stat(MDB_txn* txn) {
1094         MDB_stat result;
1095         dbi_stat(txn, handle(), &result);
1096         return result;
1097     }
1098 
1099     /**
1100    * Retrieves the flags for this database handle.
1101    *
1102    * @param txn a transaction handle
1103    * @throws lmdb_oo.MbdError on failure
1104    */
1105     uint flags(MDB_txn* txn) {
1106         uint result;
1107         dbi_flags(txn, handle(), &result);
1108         return result;
1109     }
1110 
1111     /**
1112    * Returns the number of records in this database.
1113    *
1114    * @param txn a transaction handle
1115    * @throws lmdb_oo.MbdError on failure
1116    */
1117     size_t size(MDB_txn* txn) {
1118         return stat(txn).ms_entries;
1119     }
1120 
1121     /**
1122    * @param txn a transaction handle
1123    * @param del
1124    * @throws lmdb_oo.MbdError on failure
1125    */
1126     void drop(MDB_txn* txn, const bool del = false) {
1127         dbi_drop(txn, handle(), del);
1128     }
1129 
1130     /**
1131    * Sets a custom key comparison function for this database.
1132    *
1133    * @param txn a transaction handle
1134    * @param cmp the comparison function
1135    * @throws lmdb_oo.MbdError on failure
1136    */
1137     MdbDbi set_compare(MDB_txn* txn, MDB_cmp_func* cmp = null) {
1138         dbi_set_compare(txn, handle(), cmp);
1139         return this;
1140     }
1141 
1142     /**
1143    * Retrieves a key/value pair from this database.
1144    *
1145    * @param txn a transaction handle
1146    * @param key
1147    * @param data
1148    * @throws lmdb_oo.MbdError on failure
1149    */
1150     bool get(MDB_txn* txn, const ref MdbVal key, ref MdbVal data) {
1151         return dbi_get(txn, handle(), &key._val, &data._val);
1152     }
1153 
1154     /**
1155    * Retrieves a key from this database.
1156    *
1157    * @param txn a transaction handle
1158    * @param key
1159    * @throws lmdb_oo.MbdError on failure
1160    */
1161     bool get(K)(MDB_txn* txn, const ref K key) const {
1162         const MdbVal k = new MdbVal(key, sizeof(K));
1163         MdbVal v = new MdbVal();
1164         return dbi_get(txn, handle(), k, v);
1165     }
1166 
1167     /**
1168    * Retrieves a key/value pair from this database.
1169    *
1170    * @param txn a transaction handle
1171    * @param key
1172    * @param val
1173    * @throws lmdb_oo.MbdError on failure
1174    */
1175     bool get(K, V)(MDB_txn* txn, const ref K key, ref V val) const {
1176         const MdbVal k = new MdbVal(&key, sizeof(K));
1177         MdbVal v = new MdbVal();
1178         const bool result = dbi_get(txn, handle(), k, v);
1179         if (result) {
1180             val = *v.data(V)();
1181         }
1182         return result;
1183     }
1184 
1185     /**
1186    * Retrieves a key/value pair from this database.
1187    *
1188    * @param txn a transaction handle
1189    * @param key a NUL-terminated string key
1190    * @param val
1191    * @throws lmdb_oo.MbdError on failure
1192    */
1193     bool get(V)(MDB_txn* txn, const char* key, ref V val) const {
1194         const MdbVal k = new MdbVal(key, strlen(key));
1195         MdbVal v = new MdbVal();
1196         const bool result = dbi_get(txn, handle(), k, v);
1197         if (result) {
1198             val = *v.data(V);
1199         }
1200         return result;
1201     }
1202 
1203     /**
1204    * Stores a key/value pair into this database.
1205    *
1206    * @param txn a transaction handle
1207    * @param key
1208    * @param data
1209    * @param flags
1210    * @throws lmdb_oo.MbdError on failure
1211    */
1212     bool put(MDB_txn* txn, const ref MdbVal key, ref MdbVal data, const uint flags = default_put_flags) {
1213         return dbi_put(txn, handle(), cast(MDB_val*)&key._val, cast(MDB_val*) &data._val, flags);
1214     }
1215 
1216     /**
1217    * Stores a key into this database.
1218    *
1219    * @param txn a transaction handle
1220    * @param key
1221    * @param flags
1222    * @throws lmdb_oo.MbdError on failure
1223    */
1224     bool put(K)(MDB_txn* txn, const ref K key, const uint flags = default_put_flags) {
1225         const MdbVal k = new MdbVal(&key, sizeof(K));
1226         MdbVal v = new MdbVal();
1227         return dbi_put(txn, handle(), k, v, flags);
1228     }
1229 
1230     /**
1231    * Stores a key/value pair into this database.
1232    *
1233    * @param txn a transaction handle
1234    * @param key
1235    * @param val
1236    * @param flags
1237    * @throws lmdb_oo.MbdError on failure
1238    */
1239     bool put(K, V)(MDB_txn* txn, const ref K key, const ref V val,
1240             const uint flags = default_put_flags) {
1241         const MdbVal k = new MdbVal(&key, sizeof(K));
1242         MdbVal v = new MdbVal(&val, sizeof(V));
1243         return dbi_put(txn, handle(), k, v, flags);
1244     }
1245 
1246     /**
1247    * Stores a key/value pair into this database.
1248    *
1249    * @param txn a transaction handle
1250    * @param key a NUL-terminated string key
1251    * @param val
1252    * @param flags
1253    * @throws lmdb_oo.MbdError on failure
1254    */
1255     bool put(V)(MDB_txn* txn, const char* key, const ref V val, const uint flags = default_put_flags) {
1256         const MdbVal k = new MdbVal(key, strlen(key));
1257         MdbVal v = new MdbVal(&val, sizeof(V));
1258         return dbi_put(txn, handle(), k, v, flags);
1259     }
1260 
1261   /**
1262    * Stores a key/value pair into this database.
1263    *
1264    * @param txn a transaction handle
1265    * @param key a NUL-terminated string key
1266    * @param val a NUL-terminated string key
1267    * @param flags
1268    * @throws lmdb_oo.MbdError on failure
1269    */
1270     bool put(MDB_txn* txn, const char* key, const char* val, const uint flags = default_put_flags) {
1271         const MdbVal k = new MdbVal(key, strlen(key));
1272         MdbVal v = new MdbVal(val, strlen(val));
1273         return dbi_put(txn, handle(), cast(MDB_val*)&k._val, cast(MDB_val*)&v._val, flags);
1274     }
1275 
1276   /**
1277    * Removes a key/value pair from this database.
1278    *
1279    * @param txn a transaction handle
1280    * @param key
1281    * @throws lmdb_oo.MbdError on failure
1282    */
1283     bool del(MDB_txn* txn, const ref MdbVal key) {
1284         return dbi_del(txn, handle(), cast(MDB_val*)&key._val);
1285     }
1286 
1287   /**
1288    * Removes a key/value pair from this database.
1289    *
1290    * @param txn a transaction handle
1291    * @param key
1292    * @throws lmdb_oo.MbdError on failure
1293    */
1294     bool del(K)(MDB_txn* txn, const ref K key) {
1295         const MdbVal k = new MdbVal(&key, sizeof(K));
1296         return dbi_del(txn, handle(), k);
1297     }
1298 }
1299 
1300 
1301 /* Resource Interface: Cursors */
1302 
1303 /** Resource class for `MDB_cursor*` handles.
1304  *
1305  * @note Instances of this class are movable, but not copyable.
1306  * @see http://symas.com/mdb/doc/group__internal.html#structMDB__cursor
1307  */
1308 class MdbCursor {
1309 protected:
1310     MDB_cursor* _handle; ///< Opace handle from lmdb library
1311 
1312 public:
1313     static uint default_flags; ///< Default cursor flags for ops
1314 
1315     /** Creates an LMDB MdbCursor.
1316     * @param txn the transaction handle
1317     * @param dbi the database handle
1318     * @throws lmdb_oo.MbdError on failure
1319     */
1320     static MdbCursor open(MDB_txn* txn, const MDB_dbi dbi) {
1321         MDB_cursor* handle;
1322         cursor_open(txn, dbi, &handle);
1323         assert(handle != null);
1324         return new MdbCursor(handle);
1325     }
1326 
1327     /** Constructor.
1328     * @param handle a valid `MDB_cursor*` handle
1329     */
1330     this(MDB_cursor* handle) nothrow {
1331         _handle = handle;
1332     }
1333 
1334     /** Destructor.
1335     */
1336     ~this() nothrow {
1337         try {
1338             close();
1339         } catch (Exception ex) {
1340             writefln("Catched a exception '%s'", ex.msg);
1341         }
1342     }
1343 
1344     /** Returns the underlying `MDB_cursor*` handle.
1345     */
1346     MDB_cursor* handle() nothrow {
1347       return _handle;
1348     }
1349 
1350     /** Closes this cursor.
1351    * @note this method is idempotent
1352    * @post `handle() == null`
1353    */
1354     void close() nothrow {
1355         if (_handle) {
1356             cursor_close(_handle);
1357             _handle = null;
1358         }
1359     }
1360 
1361     /** Renews this cursor.
1362      *
1363      * @param txn the transaction scope
1364      * @throws lmdb_oo.MbdError on failure
1365      */
1366     void renew(MDB_txn* txn) {
1367         cursor_renew(txn, handle());
1368     }
1369 
1370     /** Returns the cursor's transaction handle.
1371    */
1372     MDB_txn* txn() nothrow {
1373         return cursor_txn(cast(MDB_cursor*)handle());
1374     }
1375 
1376     /** Returns the cursor's database handle.
1377    */
1378     MDB_dbi dbi() nothrow {
1379         return cursor_dbi(cast(MDB_cursor*)handle());
1380     }
1381 
1382     /** Retrieves a key from the database.
1383    *
1384    * @param key
1385    * @param op
1386    * @throws lmdb_oo.MbdError on failure
1387    */
1388     bool get(MDB_val* key, const MDB_cursor_op op) {
1389         return get(key, null, op);
1390     }
1391 
1392     /** Retrieves a key from the database.
1393    *
1394    * @param key
1395    * @param op
1396    * @throws lmdb_oo.MbdError on failure
1397    */
1398     bool get(ref MdbVal key, const MDB_cursor_op op) {
1399         return get(cast(MDB_val*)&key._val, cast(MDB_val*)null, op);
1400     }
1401 
1402     /** Retrieves a key/value pair from the database.
1403    *
1404    * @param key
1405    * @param val (may be `null`)
1406    * @param op
1407    * @throws lmdb_oo.MbdError on failure
1408    */
1409     bool get(MDB_val* key, MDB_val* val, const MDB_cursor_op op) {
1410         return cursor_get(handle(), key, val, op);
1411     }
1412 
1413     /** Retrieves a key/value pair from the database.
1414    *
1415    * @param key
1416    * @param val
1417    * @param op
1418    * @throws lmdb_oo.MbdError on failure
1419    */
1420     bool get(ref MdbVal key, ref MdbVal val, const MDB_cursor_op op) {
1421         return cursor_get(handle(), cast(MDB_val*)&key._val, cast(MDB_val*)&val._val, op);
1422     }
1423 
1424     /** Retrieves a key/value pair from the database.
1425    *
1426    * @param key
1427    * @param val
1428    * @param op
1429    * @throws lmdb_oo.MbdError on failure
1430    */
1431     bool get(ref string key, ref string val, const MDB_cursor_op op) {
1432         MdbVal k = new MdbVal(), v = new MdbVal();
1433         const bool found = get(k, v, op);
1434         if (found) {
1435             char[] key0; key0  ~= to!(char[])(k._val.mv_data [ 0 .. k._val.mv_size]);
1436             key = to!string(key0);
1437             char[] val0; val0  ~= to!(char[])(v._val.mv_data [ 0 .. v._val.mv_size]);
1438             val = to!string(val0);
1439         }
1440         return found;
1441     }
1442 
1443     /** Positions this cursor at the given key.
1444    *
1445    * @param key
1446    * @param op
1447    * @throws lmdb_oo.MbdError on failure
1448    */
1449     bool find(K)(const ref K key, const MDB_cursor_op op = MDB_SET) {
1450         MdbVal k = new MdbVal(&key, sizeof(K));
1451         return get(k, null, op);
1452     }
1453 }